﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Data;
using ProgNetComponents.Collections;
using ProgNetComponents.Classes;

namespace ProgNetComponents.Design.Editors
{
    /// <summary>
    /// BaseListEditor
    /// </summary>
    public abstract class BaseListEditor : UITypeEditor 
    {
        /// <summary>
        /// editorService
        /// </summary>
        protected IWindowsFormsEditorService editorService;
        /// <summary>
        /// dropDownControl
        /// </summary>
        protected ListBox dropDownControl = null;
        /// <summary>
        /// Gets a value indicating whether [always refresh items].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [always refresh items]; otherwise, <c>false</c>.
        /// </value>
        protected virtual bool AlwaysRefreshItems
        {
            get { return false; }
        }
        /// <summary>
        /// collection
        /// </summary>
        protected NamedObjectCollection collection = new NamedObjectCollection();

        /// <summary>
        /// Gets the width of the control.
        /// </summary>
        /// <value>
        /// The width of the control.
        /// </value>
        protected virtual int ControlWidth
        {
            get { return 350; }
        }
        /// <summary>
        /// Gets the height of the control.
        /// </summary>
        /// <value>
        /// The height of the control.
        /// </value>
        protected virtual int ControlHeight
        {
            get { return 300; }
        }
        /// <summary>
        /// Gets a value indicating whether drop-down editors should be resizable by the user.
        /// </summary>
        /// <returns>true if drop-down editors are resizable; otherwise, false. </returns>
        public override bool IsDropDownResizable
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Creates the ListBox control.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="provider">The provider.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected virtual ListBox CreateControl(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            ListBox listBox  = new ListBox();
            listBox.BorderStyle = BorderStyle.None;
            listBox.Width = ControlWidth;
            listBox.Height = ControlHeight;
            listBox.SelectionMode = SelectionMode.One;
            listBox.DoubleClick += new EventHandler(listBox_DoubleClick);
            listBox.KeyDown += new KeyEventHandler(listBox_KeyDown);
            return listBox;
        }

        /// <summary>
        /// Fills the collection.
        /// </summary>
        /// <param name="collection">The collection.</param>
        /// <param name="context">The context.</param>
        /// <param name="provider">The provider.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected abstract bool FillCollection(NamedObjectCollection collection, System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value);

        /// <summary>
        /// Closes the drop down.
        /// </summary>
        protected void CloseDropDown()
        {
            if (editorService != null)
                editorService.CloseDropDown();
        }

        /// <summary>
        /// Edits the specified object's value using the editor style indicated by the <see cref="M:System.Drawing.Design.UITypeEditor.GetEditStyle" /> method.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that can be used to gain additional context information.</param>
        /// <param name="provider">An <see cref="T:System.IServiceProvider" /> that this editor can use to obtain services.</param>
        /// <param name="value">The object to edit.</param>
        /// <returns>
        /// The new value of the object. If the value of the object has not changed, this should return the same object it was passed.
        /// </returns>
        public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            editorService = provider.GetService<IWindowsFormsEditorService>();
            if (editorService != null)
            {
                if (dropDownControl == null)
                {
                    dropDownControl = CreateControl(context, provider, value);
                }
                if (AlwaysRefreshItems || collection.Count == 0)
                {
                    collection.Clear();
                    if (FillCollection(collection, context, provider, value))
                    {
                        dropDownControl.Items.Clear();
                        dropDownControl.Items.AddRange(collection.Names);
                    }
                }

                string name = GetKeyForValue(value);

                if (name != null)
                    dropDownControl.SelectedItem = name;
                editorService.DropDownControl(dropDownControl);
                NamedObject o = collection[dropDownControl.SelectedItem.AsString()];
                if (o != null)
                    return o.Value;
            }
            return null;
        }

        /// <summary>
        /// Handles the KeyDown event of the listBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="KeyEventArgs"/> instance containing the event data.</param>
        private void listBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                CloseDropDown();
        }

        /// <summary>
        /// Handles the DoubleClick event of the listBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void listBox_DoubleClick(object sender, EventArgs e)
        {
            CloseDropDown();
        }

        /// <summary>
        /// Gets the editor style used by the <see cref="M:System.Drawing.Design.UITypeEditor.EditValue(System.IServiceProvider,System.Object)" /> method.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that can be used to gain additional context information.</param>
        /// <returns>
        /// A <see cref="T:System.Drawing.Design.UITypeEditorEditStyle" /> value that indicates the style of editor used by the <see cref="M:System.Drawing.Design.UITypeEditor.EditValue(System.IServiceProvider,System.Object)" /> method. If the <see cref="T:System.Drawing.Design.UITypeEditor" /> does not support this method, then <see cref="M:System.Drawing.Design.UITypeEditor.GetEditStyle" /> will return <see cref="F:System.Drawing.Design.UITypeEditorEditStyle.None" />.
        /// </returns>
        public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown;
        }

        /// <summary>
        /// Gets the key for value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected virtual string GetKeyForValue(object value)
        {
            if (collection != null)
            {
                foreach (NamedObject kvp in collection)
                {
                    if (value.EqualsSafe(kvp.Value) || value == kvp.Value)
                        return kvp.Name;
                }
            }
            return null;
        }
    }

    
}
